package cn.vic.config.model;

import cn.vic.db.CloudPool;
import cn.vic.enums.SqlDataBaseEnum;
import cn.vic.exception.AppRuntimeException;
import cn.vic.exception.ApplicationException;
import cn.vic.util.ObjectFactory;
import cn.vic.util.messgae.Messages;
import org.apache.log4j.Logger;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DatabaseElement implements Serializable, Comparable<DatabaseElement> {

    private static final Logger LOGGER = Logger.getLogger(DatabaseElement.class);

    private static final long serialVersionUID = -4793412674735445680L;
    private String name;
    private String driverClass;
    private String connectionUrl;
    private String username;
    private String password;
    private String schema;

    private String host = ""; //主机
    private String port = "3306";//端口
    private String dataBase = ""; //库
    private SqlDataBaseEnum dataBaseEnum;

    //private Map<String, Connection> connectionMap = new HashMap<>();
    private Map<String, CloudPool> connectionPool = new HashMap<>();

    public DatabaseElement(String name, String driverClass, String connectionUrl, String username, String password,
                           String schema) {
        setName(name);
        setDriverClass(driverClass);
        setConnectionUrl(connectionUrl);
        setUsername(username);
        setPassword(password);
        setSchema(schema);
    }

    public String getUsername() {
        return username == null ? "" : username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password == null ? "" : password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getName() {
        return this.name == null ? "" : name;
    }

    public void setName(String name) {
        if (name == null || name.length() == 0) {
            throw new NullPointerException("You must specify a name!");
        }
        this.name = name;
    }

    public String getDriverClass() {
        return this.driverClass == null ? "" : driverClass;
    }

    public void setDriverClass(String driverClass) {
        if (driverClass == null) {
            throw new NullPointerException("You must specify a driver!");
        }
        this.driverClass = driverClass;
    }

    public String getConnectionUrl() {
        return this.connectionUrl == null ? "" : connectionUrl;
    }

    public void setConnectionUrl(String connectionUrl) {
        if (connectionUrl == null) {
            throw new NullPointerException("You must specify a connection URL!");
        }
        this.connectionUrl = connectionUrl;
        //进行解析URL
        analysis();
    }

    /**
     * 分析URL 获取 主机和端口及数据库类型
     */
    private void analysis() {
        dataBaseEnum = SqlDataBaseEnum.getEnumByJdbcUrl(connectionUrl);

        String url = this.connectionUrl;
        String host = null;
        String port = null;
        Matcher m = Pattern.compile("((\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3}))").matcher(url);
        while (m.find()) {
            host = m.group(1);
            break;
        }
        if (host == null && url.indexOf("localhost") > -1) host = "localhost";

        m = Pattern.compile("(\\:\\d{1,5})").matcher(url);
        while (m.find()) {
            port = m.group(1);
            break;
        }
        if (port != null) port = port.substring(1, port.length());
        if (port == null) port = dataBaseEnum.getDefaultPort();
        this.host = host;
        this.port = port;

        //获取到数据库 字符
        String temp = url.substring(url.indexOf(host) + host.length(), url.length());
        if (SqlDataBaseEnum.ORACLE.name().equals(dataBaseEnum.name())) { // Oracle
            String[] strArr = temp.split(":");
            if (strArr.length > 0) {
                dataBase = strArr[strArr.length - 1];
            }
        } else if (SqlDataBaseEnum.SQLSERVER2000.name().equals(dataBaseEnum.name()) ||
                SqlDataBaseEnum.SQLSERVER2005.name().equals(dataBaseEnum.name())) { //SQLServer
            String[] strArr = temp.split(";");
            for (String k : strArr) {
                if (k.toLowerCase().indexOf("databasename=") > -1) {
                    dataBase = k.split("=")[1];
                    break;
                }
            }
        } else {
            String[] strArr = temp.split("/");
            if (strArr.length == 2) {
                String t = strArr[1];
                int index = t.indexOf("?");
                if (index == -1) index = t.indexOf(";");
                if (index == -1) index = t.length();
                dataBase = t.substring(0, index).trim();
            }

        }

    }

    public Connection getConnection() throws InterruptedException, ApplicationException {
        if (!connectionPool.containsKey(dataBase)) {
            CloudPool pool = new CloudPool(this, dataBase);
            connectionPool.put(dataBase, pool);
            return pool.getConnection();
        }
        return connectionPool.get(dataBase).getConnection();
    }

    /**
     * 根据database 获取数据库链接
     *
     * @param database
     * @return
     */
    public Connection getConnection(String database) throws InterruptedException, ApplicationException {
        if (connectionPool.containsKey(database)) {
            Connection connection = connectionPool.get(database).getConnection();
            return connection;
        } else {
            CloudPool pool = new CloudPool(this, database);
            connectionPool.put(database, pool);
            return pool.getConnection();
        }
    }

    /**
     * 不建议使用 new 可能会导致线程池过大
     *
     * @param database
     * @return
     */
    public Connection getNewConnection(String database) {
        try {
            if (connectionPool.containsKey(database)) {
                return connectionPool.get(database).createConnection();
            } else {
                CloudPool pool = new CloudPool(this, database);
                connectionPool.put(database, pool);
                return pool.createConnection();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Connects to the database. Attempts to load the driver and connect to this instance's url.
     */
    public Connection connect(String database) throws ApplicationException {
        Driver driver = getDriver();

        Properties props = new Properties();
        props.setProperty("user", username);
        props.setProperty("password", password);

        Connection conn = null;
        try {
            //TODO 还需要写判断数据库 的拼接条件  oracle ; sqlServic ; mysql /
            // conn = driver.connect(connectionUrl + "/" + database, props);
            conn = driver.connect(dataBaseEnum.getDefaultUrl(host, port, database), props);
        } catch (SQLException e) {
            throw new AppRuntimeException("Cannot connect to database (possibly bad driver/URL combination)", e);
        }
        if (conn == null) {
            throw new AppRuntimeException("Cannot connect to database (possibly bad driver/URL combination)");
        }
        return conn;
    }

    private Driver getDriver() {
        Driver driver;
        try {
            Class<?> clazz = ObjectFactory.externalClassForName(driverClass);
            driver = (Driver) clazz.newInstance();
        } catch (Exception e) {
            LOGGER.info(e.getMessage(), e);
            throw new AppRuntimeException("Exception getting JDBC Driver", e);
        }
        return driver;
    }

    /**
     * 关闭单个 数据库连接
     *
     * @param database
     */
    public void close(String database) {
        if (connectionPool != null && connectionPool.size() > 0 && connectionPool.containsKey(database)) {
            try {
                //TODO 这里关闭有问题,无法真实关闭
                CloudPool pool = connectionPool.get(database);
                pool.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 关闭所有连接
     */
    public void allClose() {
        CloudPool[] cloudPool = new CloudPool[connectionPool.values().size()];
        cloudPool = connectionPool.values().toArray(cloudPool);
        List<CloudPool> list = Arrays.asList(cloudPool);
        for (CloudPool pool : list) {
            try {
                pool.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        connectionPool.clear();
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof DatabaseElement)) {
            return false;
        }
        if (((DatabaseElement) obj).getName().equals(name)) {
            return true;
        }
        return false;
    }

    @Override
    public int hashCode() {
        return name.hashCode();
    }

    @Override
    public int compareTo(DatabaseElement other) {
        return name.toLowerCase().compareTo(other.getName().toLowerCase());
    }

    @Override
    public String toString() {
        return this.name;
    }

    public String getSchema() {
        return schema == null ? "" : schema.toUpperCase();
    }

    public void setSchema(String schema) {
        this.schema = schema;
    }

    public String getDataBase() {
        return dataBase;
    }

    public void setDataBase(String dataBase) {
        this.dataBase = dataBase;
    }

    public SqlDataBaseEnum getDataBaseEnum() {
        return dataBaseEnum;
    }

    public String getHost() {
        return host;
    }

    public String getPort() {
        return port;
    }
}
